package com.nlina.qiniu_bigwork.controller;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;

/**
 * 自定义错误控制器
 * 处理应用中的各种错误和异常情况，提供友好的错误页面
 * 实现Spring Boot的ErrorController接口，覆盖默认的错误处理行为
 */
@Controller
public class CustomErrorController implements ErrorController {
    
    private static final Logger log = LoggerFactory.getLogger(CustomErrorController.class);
    
    /**
     * 常见HTTP状态码对应的错误消息映射
     */
    private static final Map<Integer, String> ERROR_MESSAGES = Map.of(
        HttpStatus.NOT_FOUND.value(), "请求的页面或资源不存在",
        HttpStatus.FORBIDDEN.value(), "您没有权限访问此资源",
        HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统内部错误，请稍后再试",
        HttpStatus.BAD_REQUEST.value(), "请求参数有误",
        HttpStatus.GATEWAY_TIMEOUT.value(), "系统响应超时，七牛云服务可能暂时不可用",
        HttpStatus.REQUEST_TIMEOUT.value(), "系统响应超时，七牛云服务可能暂时不可用"
    );
    
    /**
     * 配置是否包含堆栈跟踪的属性，从application配置文件中注入
     * 可选值：never, always, on_param
     */
    @Value("${server.error.include-stacktrace:never}")
    private String includeStackTrace;

    /**
     * 处理所有错误请求的主方法
     * 根据错误类型和状态码返回相应的错误页面
     * 
     * @param request HTTP请求对象，包含错误信息
     * @param model 视图模型，用于传递数据到模板
     * @return 错误页面的视图名称
     */
    @RequestMapping("/error")
    public String handleError(HttpServletRequest request, Model model) {
        // 获取错误信息
        Integer statusCode = getStatusCode(request);
        String errorMsg = getErrorAttribute(request, RequestDispatcher.ERROR_MESSAGE, "未知错误");
        Throwable exception = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
        
        // 记录错误日志
        log.error("发生错误: 状态码={}, 错误消息={}", statusCode, errorMsg);
        if (exception != null) {
            log.error("异常详情: ", exception);
        }
        
        // 添加错误信息到模型
        model.addAttribute("status", statusCode);
        model.addAttribute("error", errorMsg);
        model.addAttribute("friendlyMessage", getFriendlyMessage(statusCode));
        
        // 添加堆栈跟踪
        addStackTraceIfNeeded(model, exception);
        
        // 根据状态码选择错误页面
        if (statusCode != null) {
            if (statusCode == HttpStatus.NOT_FOUND.value()) {
                return "error/404"; // 返回404错误专用页面
            } else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
                return "error/500"; // 返回500错误专用页面
            }
        }
        
        return "error/error"; // 返回通用错误页面
    }
    
    /**
     * 从请求中获取HTTP状态码
     * 
     * @param request HTTP请求对象
     * @return HTTP状态码，如果不存在则返回null
     */
    private Integer getStatusCode(HttpServletRequest request) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        return status != null ? Integer.parseInt(status.toString()) : null;
    }
    
    /**
     * 从请求中获取指定名称的错误属性
     * 
     * @param request HTTP请求对象
     * @param name 属性名称
     * @param defaultValue 默认值，当属性不存在时返回
     * @return 属性值或默认值
     */
    private String getErrorAttribute(HttpServletRequest request, String name, String defaultValue) {
        Object value = request.getAttribute(name);
        return value != null ? value.toString() : defaultValue;
    }
    
    /**
     * 根据状态码获取用户友好的错误消息
     * 
     * @param statusCode HTTP状态码
     * @return 对应的友好错误消息
     */
    private String getFriendlyMessage(Integer statusCode) {
        if (statusCode == null) {
            return "系统遇到了问题";
        }
        return ERROR_MESSAGES.getOrDefault(statusCode, "系统遇到了问题");
    }
    
    /**
     * 根据配置决定是否将异常堆栈信息添加到模型中
     * 
     * @param model 视图模型
     * @param exception 异常对象
     */
    private void addStackTraceIfNeeded(Model model, Throwable exception) {
        if (exception != null && ("always".equals(includeStackTrace) || "on_param".equals(includeStackTrace))) {
            StringWriter sw = new StringWriter();
            exception.printStackTrace(new PrintWriter(sw));
            model.addAttribute("trace", sw.toString());
        }
    }
}